package ua.lviv.franko.solvers.stressanalysis.twoDimention;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.jzy3d.maths.Range;
import org.jzy3d.plot3d.builder.Mapper;

import ua.lviv.franko.integration.ConstantFunction2D;
import ua.lviv.franko.integration.Gauss2D;
import ua.lviv.franko.integration.IFunction2D;
import ua.lviv.franko.matrix.RowType;
import ua.lviv.franko.matrix.hash.lu.HashMatrixLUStyle;
import ua.lviv.franko.matrix.hash.lu.LUFactorization;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.fem.AbstractStressElement;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.fem.LinearRectangularElement;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.savestructures.bounderyconditions.BounderyConditionLinear;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.savestructures.bounderyconditions.BounderyConditionStressInterface;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.savestructures.bounderyconditions.BounderyConditionType;
import ua.lviv.franko.triangulation.SaveStructures.Node;
import ua.lviv.franko.triangulation.SaveStructures.NodePair;
import ua.lviv.franko.triangulation.SaveStructures.Rectangle;
import ua.lviv.franko.visualisation.Visualise3D;

/**
 * @author marko
 * 
 */
public class Solver {
	public static final int								NUMBER_OF_UNKNOWNS	= 2;

	protected static final double						EPS					= 0.000000000001;
	protected List<AbstractStressElement>				elementsList;
	protected List<BounderyConditionStressInterface>	bounderyConditions;
	protected HashMatrixLUStyle								mainMatrix;
	protected double[]									b;
	protected int										N;

	public Solver(int N, List<AbstractStressElement> elementsList, List<BounderyConditionStressInterface> bounderyConditions) {
		this.elementsList = elementsList;
		this.bounderyConditions = bounderyConditions;
		this.N = N;
	}

	public void generate() {
		generate(elementsList, bounderyConditions);
	}

	protected void setN(int n) {
		this.N = n;
	}

	protected void generate(List<AbstractStressElement> elementsList, List<BounderyConditionStressInterface> bounderyConditions) {
		// int N = elementsList.get(elementsList.size()-1).lastNumber()+1;
		mainMatrix = new HashMatrixLUStyle(RowType.ARRAY_TYPE, N * 2);
		b = new double[N * 2];

		for (Iterator<AbstractStressElement> iterator = elementsList.iterator(); iterator.hasNext();) {
			AbstractStressElement element = iterator.next();

			ElementMatrixForStress data = element.generateAll();
			List<Integer> pos = data.getNodeList();
			for (int i = 0; i < pos.size(); i++) {
				for (int j = 0; j < pos.size(); j++) {
					mainMatrix.setValue(pos.get(i) * 2, pos.get(j) * 2, mainMatrix.getValue(pos.get(i) * 2, pos.get(j) * 2) + data.matrix[2 * i][2 * j]);

					mainMatrix.setValue(pos.get(i) * 2, pos.get(j) * 2 + 1, mainMatrix.getValue(pos.get(i) * 2, pos.get(j) * 2 + 1) + data.matrix[2 * i][2 * j + 1]);
					mainMatrix.setValue(pos.get(i) * 2 + 1, pos.get(j) * 2, mainMatrix.getValue(pos.get(i) * 2 + 1, pos.get(j) * 2) + data.matrix[2 * i + 1][2 * j]);
					mainMatrix.setValue(pos.get(i) * 2 + 1, pos.get(j) * 2 + 1, mainMatrix.getValue(pos.get(i) * 2 + 1, pos.get(j) * 2 + 1) + data.matrix[2 * i + 1][2 * j + 1]);
				}
				b[pos.get(i) * 2] += data.vector[i * 2];
				b[pos.get(i) * 2 + 1] += data.vector[i * 2 + 1];
			}
		}
		
		// System.out.println("-----------------");
		// for (int i = 0; i < b.length; i++) {
		// for (int j = 0; j < b.length; j++) {
		// System.out.print(mainMatrix.getValue(i, j) + "\t");
		// }
		// System.out.print("\n");
		// // System.out.println(b[i]);
		// }
		// System.out.println("-----------------");

	}
	
	public void applyBounderyConditions(List<BounderyConditionStressInterface> bounderyConditionsLocal){
		for (BounderyConditionStressInterface cond : bounderyConditionsLocal) {
			cond.applyBounderyCondition(mainMatrix, b);
		}
	}

	public double[] solve() {
		System.out.println("-----------------");
		for (int i = 0; i < b.length; i++) {
			// for (int j = 0; j < b.length; j++) {
			// System.out.print(mainMatrix.getValue(i, j) + "\t");
			// }
			System.out.print("\n");
			System.out.println(b[i]);
		}
		System.out.println("-----------------");

		LUFactorization lu = new LUFactorization(this.mainMatrix);
		return lu.calculate(b);
	}

	public HashMatrixLUStyle getMainMatrix() {
		return mainMatrix;
	}

	public double[] getB() {
		return b;
	}

	public List<BounderyConditionStressInterface> getBounderyConditions() {
		return bounderyConditions;
	}
	
	public List<AbstractStressElement> getElementsList() {
		return elementsList;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// ArrayList<Node> nodeList = new ArrayList<Node>();
		double ax = -0.0, bx = 1.0, ay = -0.0, by = 1.0;

		int n = 13, m = 13;

		double hx = (bx - ax) / (double) n, hy = (by - ay) / (double) m;

		// вузли+функції:
		// for (int i = 0; i <= m; i++) {
		// for (int j = 0; j <= n; j++) {
		// Node p = new Node(ax + j * hx, ay + i * hy, false, i * n + j);
		// System.out.println(p.x + "\t" + p.y);
		// // if ((i==0)||(j==0)) {
		// // p.IsOnBoundery=true;
		// // }
		// // nodeList.add(p);
		// }
		// }
		// double k1 = 1;
		// double k2 = 0.5;
		// double k3 = 0.25;
		double v = 0.6;
		double e = 0.6;

		IFunction2D f1 = new ConstantFunction2D(0.0);
		IFunction2D f2 = new ConstantFunction2D(0.0);
		List<AbstractStressElement> elems = new ArrayList<AbstractStressElement>();
		Set<Node> tmpNodesSet = new HashSet<Node>();

		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				// if(i==m-1 && j==n-1) continue;
				Rectangle r = new Rectangle();
				r.A = new Node(ax + j * hx, ay, false, i * (n + 1) + j);
				r.B = new Node(ax + (j + 1) * hx, ay, false, i * (n + 1) + j + 1);
				r.C = new Node(ax + (j + 1) * hx, ay + hy, false, (i + 1) * (n + 1) + j + 1);
				r.D = new Node(ax + j * hx, ay + hy, false, (i + 1) * (n + 1) + j);
				tmpNodesSet.add(r.A);
				tmpNodesSet.add(r.B);
				tmpNodesSet.add(r.C);
				tmpNodesSet.add(r.D);
				// if ((i==0)||(i==n)) {
				// r.A.IsOnBoundery=true;
				// }

				// LinearRectangularElement elem = new LinearRectangularElement(r.A, r.B, r.C, r.D, k1, k2, k3, f1, f2);
				LinearRectangularElement elem = new LinearRectangularElement(r.A, r.B, r.C, r.D, e, v, f1, f2);
				// EnrichedLinearRectangularElement elem = new
				// EnrichedLinearRectangularElement(i*(n+1)+j, i*(n+1)+j+1,
				// (i+1)*(n+1)+j+1, (i+1)*(n+1)+j, r.A, r.B, r.C, r.D,eParams,
				// 3, 3);
				elems.add(elem);
			}
			ay += hy;
		}
		ay = -0.0;
		List<Node> nodes = new ArrayList<Node>();
		nodes.addAll(tmpNodesSet);
		Collections.sort(nodes);
		List<BounderyConditionStressInterface> bounderies = new ArrayList<BounderyConditionStressInterface>();
		List<NodePair> leftNodePairs = new ArrayList<NodePair>();
		List<NodePair> rightNodePairs = new ArrayList<NodePair>();
		List<NodePair> topNodePairs = new ArrayList<NodePair>();
		List<NodePair> bottomNodePairs = new ArrayList<NodePair>();
		for (AbstractStressElement element : elems) {
			List<Integer> positions = element.getPos();
			Node A = nodes.get(positions.get(0));
			Node B = nodes.get(positions.get(1));
			Node C = nodes.get(positions.get(2));
			Node D = nodes.get(positions.get(3));
			// left
			List<Node> tmpNodes = new ArrayList<Node>();
			if (Math.abs(A.x - ax) < EPS) {
				tmpNodes.add(A);
			}
			if (Math.abs(B.x - ax) < EPS) {
				tmpNodes.add(B);
			}
			if (Math.abs(C.x - ax) < EPS) {
				tmpNodes.add(C);
			}
			if (Math.abs(D.x - ax) < EPS) {
				tmpNodes.add(D);
			}

			if (tmpNodes.size() == 2) {
				leftNodePairs.add(new NodePair(tmpNodes.get(0), tmpNodes.get(1)));
			}

			// right
			tmpNodes.clear();
			if (Math.abs(A.x - bx) < EPS) {
				tmpNodes.add(A);
			}
			if (Math.abs(B.x - bx) < EPS) {
				tmpNodes.add(B);
			}
			if (Math.abs(C.x - bx) < EPS) {
				tmpNodes.add(C);
			}
			if (Math.abs(D.x - bx) < EPS) {
				tmpNodes.add(D);
			}

			if (tmpNodes.size() == 2) {
				rightNodePairs.add(new NodePair(tmpNodes.get(0), tmpNodes.get(1)));
			}

			// top
			tmpNodes.clear();
			if (Math.abs(A.y - by) < EPS) {
				tmpNodes.add(A);
			}
			if (Math.abs(B.y - by) < EPS) {
				tmpNodes.add(B);
			}
			if (Math.abs(C.y - by) < EPS) {
				tmpNodes.add(C);
			}
			if (Math.abs(D.y - by) < EPS) {
				tmpNodes.add(D);
			}

			if (tmpNodes.size() == 2) {
				topNodePairs.add(new NodePair(tmpNodes.get(0), tmpNodes.get(1)));
			}

			// bottom
			tmpNodes.clear();
			if (Math.abs(A.y - ay) < EPS) {
				tmpNodes.add(A);
			}
			if (Math.abs(B.y - ay) < EPS) {
				tmpNodes.add(B);
			}
			if (Math.abs(C.y - ay) < EPS) {
				tmpNodes.add(C);
			}
			if (Math.abs(D.y - ay) < EPS) {
				tmpNodes.add(D);
			}

			if (tmpNodes.size() == 2) {
				bottomNodePairs.add(new NodePair(tmpNodes.get(0), tmpNodes.get(1)));
			}
		}
		Gauss2D gauss2d = new Gauss2D(7);

		bounderies.add(new BounderyConditionLinear(BounderyConditionType.DIRIHLET_TYPE, new ConstantFunction2D(0.0), new ConstantFunction2D(0.0), leftNodePairs, gauss2d));

		bounderies.add(new BounderyConditionLinear(BounderyConditionType.DIRIHLET_TYPE, new ConstantFunction2D(1.0), new ConstantFunction2D(0.0), rightNodePairs, gauss2d));

		Solver s = new Solver((n + 1) * (m + 1), elems, bounderies);
		s.generate();
		s.applyBounderyConditions(bounderies);
		double[] res = s.solve();
		for (int k = 0; k < res.length; k++) {
			System.out.println(res[k]);
		}
		System.out.println();
		System.out.print("new double[]{");
		for (int k = 0; k < res.length; k++) {
			System.out.print(res[k] + ", ");
		}
		System.out.println("}");
		final List<AbstractStressElement> el = elems;
		final double[] vals = res;

		Visualise3D.show(new Mapper() {
			@Override
			public double f(double x, double y) {
				double val = 0.0;
				int i = 0;
				while (val == 0.0 && i < el.size()) {
					val += el.get(i).calculateU(x, y, vals);
					i++;
				}

				// return ff.calculateDerivatyY(x, y);//(x, y);
				return val;
			}
		}, new Range(ax, bx), new Range(ay, by));

		Visualise3D.show(new Mapper() {
			@Override
			public double f(double x, double y) {
				double val = 0.0;
				int i = 0;
				while (val == 0.0 && i < el.size()) {
					val += el.get(i).calculateV(x, y, vals);
					i++;
				}

				// return ff.calculateDerivatyY(x, y);//(x, y);
				return val;
			}
		}, new Range(ax, bx), new Range(ay, by));

		Visualise3D.show(new Mapper() {
			@Override
			public double f(double x, double y) {
				double val = 0.0;
				int i = 0;
				while (val == 0.0 && i < el.size()) {
					val += el.get(i).calculateSigma22(x, y, vals);
					i++;
				}

				// return ff.calculateDerivatyY(x, y);//(x, y);
				return val;
			}
		}, new Range(ax, bx), new Range(ay, by));

	}
}
